/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file flash_drv.c
 * @brief This file defines flash driver functions
 */

/*******Includes***************************************************************/
#include "flash_reg_access.h"

/*******Definitions************************************************************/
#define FLASH_PFLASH_BASE_ADDRESS                   (0x10000000U)
#define FLASH_DFLASH_BASE_ADDRESS                   (0x18000000U)


#define FLASH_RP_WP_OPB_BASE_ADDRESS                (0x1C000000U)
#if (CONFIG_FLASH_NUMBER_OF_OPB_FIELD != 1U)
#define FLASH_DGB_KEY_OPB_BASE_ADDRESS              (0x1C200000U)
#define FLASH_AB_SWAP_OPB_BASE_ADDRESS              (0x1C400000U)
#endif


#define FLASH_ADDRESS_ALIGMENT                      (8U)
#define FLASH_PHRASE_SIZE                           (8U)
#define FLASH_SETCTOIN_PROGRAM_MAX_BYTES            (512U)

#if (CONFIG_FLASH_PFLASH_SIZE == 0x20000U)
/* Pflash size 128KB */
#define FLASH_OPTION_BYTE_PFLASH_WP0_OFFSET         (0x8U)
#define FLASH_OPTION_BYTE_PFLASH_WP1_OFFSET         (0x10U)
#define FLASH_OPTION_BYTE_DFLASH_WP0_OFFSET         (0x28U)
#elif (CONFIG_FLASH_PFLASH_SIZE == 0x40000U)
/* Pflash size 256KB */
#define FLASH_OPTION_BYTE_PFLASH_WP0_OFFSET         (0x8U)
#define FLASH_OPTION_BYTE_PFLASH_WP1_OFFSET         (0x10U)
#define FLASH_OPTION_BYTE_PFLASH_WP2_OFFSET         (0x18U)
#define FLASH_OPTION_BYTE_PFLASH_WP3_OFFSET         (0x20U)
#define FLASH_OPTION_BYTE_DFLASH_WP0_OFFSET         (0x28U)
#elif (CONFIG_FLASH_PFLASH_SIZE == 0x80000U)
/* Pflash size 512KB */
#define FLASH_OPTION_BYTE_PFLASH_WP0_OFFSET         (0x8U)
#define FLASH_OPTION_BYTE_PFLASH_WP1_OFFSET         (0x10U)
#define FLASH_OPTION_BYTE_PFLASH_WP2_OFFSET         (0x18U)
#define FLASH_OPTION_BYTE_PFLASH_WP3_OFFSET         (0x20U)
#define FLASH_OPTION_BYTE_PFLASH_WP4_OFFSET         (0x28U)
#define FLASH_OPTION_BYTE_PFLASH_WP5_OFFSET         (0x30U)
#define FLASH_OPTION_BYTE_PFLASH_WP6_OFFSET         (0x38U)
#define FLASH_OPTION_BYTE_PFLASH_WP7_OFFSET         (0x40U)
#define FLASH_OPTION_BYTE_DFLASH_WP0_OFFSET         (0x88U)
#define FLASH_OPTION_BYTE_DFLASH_WP1_OFFSET         (0x90U)
#elif (CONFIG_FLASH_PFLASH_SIZE == 0x100000U)
/* Pflash size 1024KB */
#define FLASH_OPTION_BYTE_PFLASH_WP0_OFFSET         (0x8U)
#define FLASH_OPTION_BYTE_PFLASH_WP1_OFFSET         (0x10U)
#define FLASH_OPTION_BYTE_PFLASH_WP2_OFFSET         (0x18U)
#define FLASH_OPTION_BYTE_PFLASH_WP3_OFFSET         (0x20U)
#define FLASH_OPTION_BYTE_PFLASH_WP4_OFFSET         (0x28U)
#define FLASH_OPTION_BYTE_PFLASH_WP5_OFFSET         (0x30U)
#define FLASH_OPTION_BYTE_PFLASH_WP6_OFFSET         (0x38U)
#define FLASH_OPTION_BYTE_PFLASH_WP7_OFFSET         (0x40U)
#define FLASH_OPTION_BYTE_PFLASH_WP8_OFFSET         (0x48U)
#define FLASH_OPTION_BYTE_PFLASH_WP9_OFFSET         (0x50U)
#define FLASH_OPTION_BYTE_PFLASH_WP10_OFFSET        (0x58U)
#define FLASH_OPTION_BYTE_PFLASH_WP11_OFFSET        (0x60U)
#define FLASH_OPTION_BYTE_PFLASH_WP12_OFFSET        (0x68U)
#define FLASH_OPTION_BYTE_PFLASH_WP13_OFFSET        (0x70U)
#define FLASH_OPTION_BYTE_PFLASH_WP14_OFFSET        (0x78U)
#define FLASH_OPTION_BYTE_PFLASH_WP15_OFFSET        (0x80U)
#define FLASH_OPTION_BYTE_DFLASH_WP0_OFFSET         (0x88U)
#define FLASH_OPTION_BYTE_DFLASH_WP1_OFFSET         (0x90U)
#endif

#if (CONFIG_FLASH_AB_SWAP_FEATURE == 1U)
#define FLASH_AB_SWAP_ENABLE_KEY                    (0xA5U)
#define FLASH_OPTION_BYTE_SWAP_MAPPING_SEL          (0x8U)

#define FLASH_AB_SWAP_REMAP_B_KEY                   (0xA5U)
#endif

#define FLASH_READ_PROTECTION_KEY                   (0xA5U)


/* Flash command */
#define FLASH_ERASE_BLOCK_CMD                       (0x1U)
#define FLASH_ERASE_PAGE_CMD                        (0x2U)
#define FLASH_ERASE_OB_CMD                          (0x3U)
#define FLASH_PROGRAM_PHRASE_CMD                    (0x4U)
#define FLASH_PROGRAM_SECTION_CMD                   (0x5U)
#define FLASH_PROGRAM_OB_PHRASE_CMD                 (0x6U)
#define FLASH_VERIFY_BLOCK_CMD                      (0x7U)
#define FLASH_VERIFY_PAGE_CMD                       (0x8U)

#define FLASH_HW_DEADLINE_TIMEOUT                   (0x40000U)
#define FLASH_PFLASH_BLOCK_SIZE_FOR_1M              (0x80000)
#define FLASH_PFLASH_BLOCK_SIZE_FOR_512K            (0x40000)

/*******Variables**************************************************************/
/*! @brief Table to save register base addresses for flash controller */
static FLASH_Type* const s_flashRegBase = CONFIG_FLASH_REG_BASE_PTRS;

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/
static errcode_t FLASH_Erase(FLASH_Type* regBase, uint32_t address, uint8_t eraseCmd);
static errcode_t FLASH_Verify(FLASH_Type* regBase, uint32_t address, uint8_t eraseCmd);
static errcode_t FLASH_StartCmd(FLASH_Type* regBase);
static errcode_t FLASH_WaitOperateComplete(FLASH_Type* regBase);

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : FLASH_SetFlashClock
 *** Description   : Set flash frequency
 ******************************************************************************/
void FLASH_SetFlashClock(uint16_t freq)
{
    FLASH_Type* regBase = s_flashRegBase;

    FLASH_REG_SetFlashFrequency(regBase, freq);
}

/*******************************************************************************
 *** Function Name : FLASH_Unlock
 *** Description   : Unlock the flash Controller
 ******************************************************************************/
void FLASH_Unlock(flash_area_type_t area)
{
    FLASH_Type* regBase = s_flashRegBase;

    switch (area) {
        case FLASH_AREA_PFLASH:
            /* Unlock program flash controller */
            FLASH_REG_UnlockPflash(regBase);
            break;

        case FLASH_AREA_DFLASH:
            /* Unlock data flash controller */
            FLASH_REG_UnlockDflash(regBase);
            break;

        case FLASH_AREA_OPTION_BYTE:
            /* Unlock option byte controller */
            FLASH_REG_UnlockOptionByte(regBase);
            break;

        default:
            break;
    }

}

/*******************************************************************************
 *** Function Name : FLASH_Lock
 *** Description   : Lock the flash Controller
 ******************************************************************************/
void FLASH_Lock(flash_area_type_t area)
{
    FLASH_Type* regBase = s_flashRegBase;

    switch (area) {
        case FLASH_AREA_PFLASH:
            /* Lock program flash controller */
            FLASH_REG_LockPflash(regBase);
            break;

        case FLASH_AREA_DFLASH:
            /* Lock data flash controller */
            FLASH_REG_LockDflash(regBase);
            break;

        case FLASH_AREA_OPTION_BYTE:
            /* Lock option byte controller */
            FLASH_REG_LockOptionByte(regBase);
            break;

        default:
            break;
    }
}

/*******************************************************************************
 *** Function Name : FLASH_ReadWord
 *** Description   : Read word from the flash specified address
 ******************************************************************************/
uint32_t FLASH_ReadWord(uint32_t address)
{
#if (CONFIG_FLASH_NUMBER_OF_OPB_FIELD == 1U)
    ASSERT_PARAM((address >= FLASH_PFLASH_BASE_ADDRESS
                  && address <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE))
                 || (address >= FLASH_DFLASH_BASE_ADDRESS
                     && address <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE))
                 || (address >= FLASH_RP_WP_OPB_BASE_ADDRESS
                     && address <= FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE));
#else
    ASSERT_PARAM((address >= FLASH_PFLASH_BASE_ADDRESS
                  && address <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE))
                 || (address >= FLASH_DFLASH_BASE_ADDRESS
                     && address <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE))
                 || (address >= FLASH_RP_WP_OPB_BASE_ADDRESS
                     && address <= FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE)
                 || (address >= FLASH_DGB_KEY_OPB_BASE_ADDRESS
                     && address <= FLASH_DGB_KEY_OPB_BASE_ADDRESS + CONFIG_FLASH_DGB_KEY_OPB_SIZE)
                 || (address >= FLASH_RP_WP_OPB_BASE_ADDRESS
                     && address <= FLASH_AB_SWAP_OPB_BASE_ADDRESS + CONFIG_FLASH_AB_SWAP_OPB_SIZE));
#endif

    uint32_t data;

    /* Read word of the specified address */
    data = *(volatile uint32_t*)(address);

    return  data;
}

/*******************************************************************************
 *** Function Name : FLASH_Read
 *** Description   : Read data from the flash specified address
 ******************************************************************************/
void FLASH_Read(uint32_t startAddress, uint8_t* dataBuffer, uint32_t size)
{
#if (CONFIG_FLASH_NUMBER_OF_OPB_FIELD == 1U)
    ASSERT_PARAM((startAddress >= FLASH_PFLASH_BASE_ADDRESS
                  && startAddress + size <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE))
                 || (startAddress >= FLASH_DFLASH_BASE_ADDRESS
                     && startAddress + size <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE))
                 || (startAddress >= FLASH_RP_WP_OPB_BASE_ADDRESS
                     && startAddress + size <= FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE));
#else
    ASSERT_PARAM((startAddress >= FLASH_PFLASH_BASE_ADDRESS
                  && startAddress + size <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE))
                 || (startAddress >= FLASH_DFLASH_BASE_ADDRESS
                     && startAddress + size <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE))
                 || (startAddress >= FLASH_RP_WP_OPB_BASE_ADDRESS
                     && startAddress + size <= FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE)
                 || (startAddress >= FLASH_DGB_KEY_OPB_BASE_ADDRESS
                     && startAddress + size <= FLASH_DGB_KEY_OPB_BASE_ADDRESS + CONFIG_FLASH_DGB_KEY_OPB_SIZE)
                 || (startAddress >= FLASH_AB_SWAP_OPB_BASE_ADDRESS
                     && startAddress + size <= FLASH_AB_SWAP_OPB_BASE_ADDRESS + CONFIG_FLASH_AB_SWAP_OPB_SIZE)
                );
#endif

    ASSERT_PARAM(dataBuffer != NULL);

    uint32_t i;
    uint32_t address = startAddress;

    for (i = 0U; i < size; i++) {
        dataBuffer[i] = *(volatile uint8_t*)(address);
        address++;
    }
}

/*******************************************************************************
 *** Function Name : FLASH_Program
 *** Description   : Programs a phrase(8 byte) at a specified address
 ******************************************************************************/
errcode_t FLASH_Program(uint32_t address, uint32_t dataLow, uint32_t dataHigh)
{
    ASSERT_PARAM((address >= FLASH_PFLASH_BASE_ADDRESS
                  && address + FLASH_PHRASE_SIZE <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE))
                 || (address >= FLASH_DFLASH_BASE_ADDRESS
                     && address + FLASH_PHRASE_SIZE <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE)));

    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif

    /* Check whether the address is aligned */
    if (((address & (FLASH_ADDRESS_ALIGMENT - 1U)) == 0U)) {
        /* Wait for last operation to complete */
        (void)FLASH_WaitOperateComplete(regBase);

        /* Clear error flags before programing */
        FLASH_REG_ClearAllErrFlag(regBase);

#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        FLASH_REG_SafetyUnlock(regBase);

        /* Get control of flash controller */
        do {
            FLASH_REG_GetControlOfFlashController(regBase);
            timeout--;
        } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

        /* Check whether control of the Flash controller has been got */
        if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
            /* Set programing address and cmd*/
            FLASH_REG_SetFlashCmd(regBase, FLASH_PROGRAM_PHRASE_CMD);
            FLASH_REG_SetCmdAddress(regBase, address);

            /* Write low data and high data to DATAL and DATAH register */
            FLASH_REG_WriteLowWord(regBase, dataLow);
            FLASH_REG_WriteHighWord(regBase, dataHigh);

            /* Start command, programing begin */
            errCode = FLASH_StartCmd(regBase);
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        } else {
            errCode = ERR_ERROR;
        }
#endif
    } else {
        errCode = ERR_FLASH_PARAMETER_ERROR;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_ProgramSection
 *** Description   : Program section(Maximum 512 byte) at a specified address
 ******************************************************************************/
errcode_t FLASH_ProgramSection(uint32_t startAddress, uint8_t* buffer, uint16_t length)
{
    ASSERT_PARAM((startAddress >= FLASH_PFLASH_BASE_ADDRESS
                  && startAddress + length <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE))
                 || (startAddress >= FLASH_DFLASH_BASE_ADDRESS
                     && startAddress + length <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE)));
    ASSERT_PARAM(buffer != NULL);

    FLASH_Type* regBase = s_flashRegBase;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t lowWord;
    uint32_t highWord;
    uint16_t i;
    uint8_t lengthOfPhrase;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif

    /* Check whether the address is aligned and the length is less than 512 */
    if (((startAddress & (FLASH_ADDRESS_ALIGMENT - 1U)) == 0U) && (length <= FLASH_SETCTOIN_PROGRAM_MAX_BYTES)
            && ((length & (FLASH_ADDRESS_ALIGMENT - 1U)) == 0U)) {

        /* Wait for last operation to complete */
        (void)FLASH_WaitOperateComplete(regBase);

        /* Clear error flags before programing */
        FLASH_REG_ClearAllErrFlag(regBase);

#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        FLASH_REG_SafetyUnlock(regBase);

        /* Get control of flash controller */
        do {
            FLASH_REG_GetControlOfFlashController(regBase);
            timeout--;
        } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

        /* Check whether control of the Flash controller has been got */
        if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
            FLASH_REG_ClearPointOfInternelBuffer(regBase);

            /* Set cmd programing address and length of phrase*/
            FLASH_REG_SetFlashCmd(regBase, FLASH_PROGRAM_SECTION_CMD);
            FLASH_REG_SetCmdAddress(regBase, startAddress);
            lengthOfPhrase = (uint8_t)(length / FLASH_PHRASE_SIZE);
            FLASH_REG_SetProgramLengthOfPhrase(regBase, (lengthOfPhrase - 1U));

            for (i = 0; i < length; i = i + 8) {
                /* Write data to flash internal buffer */
                lowWord = (uint32_t)(buffer[i + 3] << 24U) | (uint32_t)(buffer[i + 2] << 16U)
                          | (uint32_t)(buffer[i + 1] << 8U) | (uint32_t)buffer[i];
                highWord = (uint32_t)(buffer[i + 7] << 24U) | (uint32_t)(buffer[i + 6] << 16U)
                           | (uint32_t)(buffer[i + 5] << 8U) | (uint32_t)buffer[i + 4];
                FLASH_REG_WriteLowWord(regBase, lowWord);
                FLASH_REG_WriteHighWord(regBase, highWord);
            }

            /* Start command, programing begin */
            errCode = FLASH_StartCmd(regBase);
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        } else {
            errCode = ERR_ERROR;
        }
#endif
    } else {
        /* The address or length is not aligned */
        errCode = ERR_FLASH_PARAMETER_ERROR;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_ProgramOptionByte
 *** Description   : Programs page at a specified address
 ******************************************************************************/
errcode_t FLASH_ProgramOptionByte(uint32_t address, uint8_t* buffer, uint16_t length)
{
#if (CONFIG_FLASH_NUMBER_OF_OPB_FIELD == 1U)
    ASSERT_PARAM((address >= FLASH_RP_WP_OPB_BASE_ADDRESS
                  && address + length <= (FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE)));
#else
    ASSERT_PARAM((address >= FLASH_RP_WP_OPB_BASE_ADDRESS
                  && address + length <= (FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE))
                 || (address >= FLASH_DGB_KEY_OPB_BASE_ADDRESS
                     && address + length <= (FLASH_DGB_KEY_OPB_BASE_ADDRESS + CONFIG_FLASH_DGB_KEY_OPB_SIZE))
                 || (address >= FLASH_AB_SWAP_OPB_BASE_ADDRESS
                     && address + length <= (FLASH_AB_SWAP_OPB_BASE_ADDRESS + CONFIG_FLASH_AB_SWAP_OPB_SIZE)));
#endif
    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;
    uint32_t dataLow;
    uint32_t dataHigh;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif

    /* Check whether the address is aligned and the length is less than 512 */
    if (((address & (FLASH_ADDRESS_ALIGMENT - 1U)) == 0U) && ((length & (FLASH_ADDRESS_ALIGMENT - 1U)) == 0U)) {

        /* Wait for last operation to complete */
        (void)FLASH_WaitOperateComplete(regBase);

        /* Clear error flags before programing */
        FLASH_REG_ClearAllErrFlag(regBase);

        while ((length > 0U) && (errCode == ERR_SUCCESS)) {
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
            FLASH_REG_SafetyUnlock(regBase);

            /* Get control of flash controller */
            do {
                FLASH_REG_GetControlOfFlashController(regBase);
                timeout--;
            } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

            /* Check whether control of the Flash controller has been got */
            if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
                /* Set programing command and address */
                FLASH_REG_SetFlashCmd(regBase, FLASH_PROGRAM_OB_PHRASE_CMD);
                FLASH_REG_SetCmdAddress(regBase, address);

                /* Write low data and high data to DATAL and DATAH register */
                dataLow = (uint32_t)(buffer[3] << 24U) | (uint32_t)(buffer[2] << 16U)
                          | (uint32_t)(buffer[1] << 8U) | (uint32_t)buffer[0];
                dataHigh = (uint32_t)(buffer[7] << 24U) | (uint32_t)(buffer[6] << 16U)
                           | (uint32_t)(buffer[5] << 8U) | (uint32_t)buffer[4];
                FLASH_REG_WriteLowWord(regBase, dataLow);
                FLASH_REG_WriteHighWord(regBase, dataHigh);

                /* Start command, programing begin */
                errCode = FLASH_StartCmd(regBase);

                length = length - FLASH_PHRASE_SIZE;
                address = address + FLASH_PHRASE_SIZE;
                buffer = buffer + FLASH_PHRASE_SIZE;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
            } else {
                errCode = ERR_ERROR;
            }
#endif
        }
    } else {
        /* The address or length is not aligned */
        errCode = ERR_FLASH_PARAMETER_ERROR;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_ErasePage
 *** Description   : Erases a specified flash page
 ******************************************************************************/
errcode_t FLASH_ErasePage(uint32_t pageAddress)
{
    ASSERT_PARAM((pageAddress >= FLASH_PFLASH_BASE_ADDRESS
                  && pageAddress <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE - 1U))
                 || (pageAddress >= FLASH_DFLASH_BASE_ADDRESS
                     && pageAddress <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE - 1U)));

    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;

    /* Erase page */
    errCode = FLASH_Erase(regBase, pageAddress, FLASH_ERASE_PAGE_CMD);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_EraseOptionByte
 *** Description   : Erases option byte
 ******************************************************************************/
errcode_t FLASH_EraseOptionByte(uint32_t address)
{
#if (CONFIG_FLASH_NUMBER_OF_OPB_FIELD == 1U)
    ASSERT_PARAM((address >= FLASH_RP_WP_OPB_BASE_ADDRESS
                  && address <= FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE));
#else
    ASSERT_PARAM((address >= FLASH_RP_WP_OPB_BASE_ADDRESS
                  && address <= FLASH_RP_WP_OPB_BASE_ADDRESS + CONFIG_FLASH_RP_WP_OPB_SIZE)
                 || (address >= FLASH_DGB_KEY_OPB_BASE_ADDRESS
                     && address <= FLASH_DGB_KEY_OPB_BASE_ADDRESS + CONFIG_FLASH_DGB_KEY_OPB_SIZE)
                 || (address >= FLASH_AB_SWAP_OPB_BASE_ADDRESS
                     && address <= FLASH_AB_SWAP_OPB_BASE_ADDRESS + CONFIG_FLASH_AB_SWAP_OPB_SIZE));
#endif

    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;

    /* Erase option byte */
    errCode = FLASH_Erase(regBase, address, FLASH_ERASE_OB_CMD);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_EraseBlock
 *** Description   : Erases block
 ******************************************************************************/
errcode_t FLASH_EraseBlock(flash_block_type_t block)
{
    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;
    uint32_t address;
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    uint16_t pflashSize;
#endif

    if (block == PFLASH_BLOCK0) {
        address = FLASH_PFLASH_BASE_ADDRESS;
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    } else if (block == PFLASH_BLOCK1) {
        pflashSize = FLASH_REG_GetChipPflashSizeInfo(regBase);

        if (pflashSize == 0x400) {
            /* For 1MB Flash */
            address = FLASH_PFLASH_BASE_ADDRESS + FLASH_PFLASH_BLOCK_SIZE_FOR_1M;
        } else if (pflashSize == 0x200) {
            /* For 512KB Flash */
            address = FLASH_PFLASH_BASE_ADDRESS + FLASH_PFLASH_BLOCK_SIZE_FOR_512K;
        } else {
            /* Nothing to do */
        }
#endif
    } else {
        address = FLASH_DFLASH_BASE_ADDRESS;
    }

    /* Erase flash block */
    errCode = FLASH_Erase(regBase, address, FLASH_ERASE_BLOCK_CMD);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_VerifyPage
 *** Description   : Verify a specified flash page if is erased successfully
 ******************************************************************************/
errcode_t FLASH_VerifyPage(uint32_t pageAddress)
{
    ASSERT_PARAM((pageAddress >= FLASH_PFLASH_BASE_ADDRESS
                  && pageAddress <= (FLASH_PFLASH_BASE_ADDRESS + CONFIG_FLASH_PFLASH_SIZE - 1U))
                 || (pageAddress >= FLASH_DFLASH_BASE_ADDRESS
                     && pageAddress <= (FLASH_DFLASH_BASE_ADDRESS + CONFIG_FLASH_DFLASH_SIZE - 1U)));

    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;

    /* Verify page */
    errCode = FLASH_Verify(regBase, pageAddress, FLASH_VERIFY_PAGE_CMD);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_VerifyBlock
 *** Description   : Verify all pflash page if is erased successfully
 ******************************************************************************/
errcode_t FLASH_VerifyBlock(flash_block_type_t block)
{
    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;
    uint32_t address;
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    uint16_t pflashSize;
#endif

    if (block == PFLASH_BLOCK0) {
        address = FLASH_PFLASH_BASE_ADDRESS;
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    } else if (block == PFLASH_BLOCK1) {
        pflashSize = FLASH_REG_GetChipPflashSizeInfo(regBase);

        if (pflashSize == 0x400) {
            /* For 1MB Flash */
            address = FLASH_PFLASH_BASE_ADDRESS + FLASH_PFLASH_BLOCK_SIZE_FOR_1M;
        } else if (pflashSize == 0x200) {
            /* For 512KB Flash */
            address = FLASH_PFLASH_BASE_ADDRESS + FLASH_PFLASH_BLOCK_SIZE_FOR_512K;
        } else {
            /* Nothing to do */
        }
#endif
    } else {
        address = FLASH_DFLASH_BASE_ADDRESS;
    }

    /* Verify flash block */
    errCode = FLASH_Verify(regBase, address, FLASH_VERIFY_BLOCK_CMD);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_SetWriteProtect
 *** Description   : Enable/Disable write protection specified pages
 ******************************************************************************/
errcode_t FLASH_SetWriteProtect(flash_block_type_t area, uint32_t startPageNumber, uint32_t pageLength, bool enable)
{
    uint32_t optionByte[CONFIG_FLASH_RP_WP_OPB_SIZE / 4U];
    uint8_t bytePosition;
    uint8_t bytePositionOffset;
    uint32_t bitPosition;
    uint32_t numberOfBlockPage;
    uint32_t i;
    errcode_t errCode = ERR_SUCCESS;

    if (area == PFLASH_BLOCK0) {
        bytePositionOffset = (FLASH_OPTION_BYTE_PFLASH_WP0_OFFSET / 4U);
        numberOfBlockPage = CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK;
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    } else if (area == PFLASH_BLOCK1) {
        bytePositionOffset = (FLASH_OPTION_BYTE_PFLASH_WP8_OFFSET / 4U);
        numberOfBlockPage = CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK;
#endif
    } else {
        bytePositionOffset = (FLASH_OPTION_BYTE_DFLASH_WP0_OFFSET / 4U);
        numberOfBlockPage = CONFIG_DFLASH_NUMBER_OF_PAGE;
    }

    /* Record current option byte */
    FLASH_Read(FLASH_RP_WP_OPB_BASE_ADDRESS, (uint8_t*)optionByte, CONFIG_FLASH_RP_WP_OPB_SIZE);

    FLASH_Unlock(FLASH_AREA_OPTION_BYTE);

    if ((startPageNumber + pageLength) <= numberOfBlockPage) {
        for (i = startPageNumber; i < startPageNumber + pageLength; i++) {
            /* Get the byte position of the current page */
            /* Byte position need to be doubled because of nWP(option byte inverse code) and offset */
            bytePosition = (uint8_t)((i >> 5U) << 1U) + bytePositionOffset;
            /* Get the bit position of the current page */
            bitPosition = i & 0x1FU;
            /* Set write protect mask */
            enable ? (optionByte[bytePosition] &= (~(uint32_t)(1U << bitPosition)))
            : (optionByte[bytePosition] |= ((uint32_t)(1U << bitPosition)));
        }

        /* Erase option byte before programing */
        errCode = FLASH_EraseOptionByte(FLASH_RP_WP_OPB_BASE_ADDRESS);

        if (errCode == ERR_SUCCESS) {
            /* Program option byte */
            errCode = FLASH_ProgramOptionByte(FLASH_RP_WP_OPB_BASE_ADDRESS,
                                              (uint8_t*)optionByte, CONFIG_FLASH_RP_WP_OPB_SIZE);
        }
    } else {
        /* StartPageNumber + pageLength is out of range */
        errCode = ERR_FLASH_PARAMETER_ERROR;
    }

    FLASH_Lock(FLASH_AREA_OPTION_BYTE);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_EnableWriteProtectRegister
 *** Description   : Enable write protection specified pages by register
 ******************************************************************************/
errcode_t FLASH_EnableWriteProtectRegister(flash_block_type_t area, uint32_t startPageNumber, uint32_t pageLength)
{
    uint32_t pflashWriteProtectMask[CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK / 32U];
    uint32_t dflashWriteProtectMask[CONFIG_DFLASH_NUMBER_OF_PAGE / 32U];
    uint32_t numberOfBlockPage;
    uint32_t i, j;
    uint8_t bytePosition;
    uint32_t bitPosition;
    FLASH_Type* regBase = s_flashRegBase;
    errcode_t errCode = ERR_SUCCESS;

    /* Initialize array */
    for (i = 0U; i < CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK / 32U; i++) {
        pflashWriteProtectMask[i] = 0U;
    }

    for (i = 0U; i < CONFIG_DFLASH_NUMBER_OF_PAGE / 32U; i++) {
        dflashWriteProtectMask[i] = 0U;
    }

    if (area == PFLASH_BLOCK0) {
        numberOfBlockPage = CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK;
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    } else if (area == PFLASH_BLOCK1) {
        numberOfBlockPage = CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK;
#endif
    } else {
        numberOfBlockPage = CONFIG_DFLASH_NUMBER_OF_PAGE;
    }

    if ((startPageNumber + pageLength) <= numberOfBlockPage) {
        for (i = startPageNumber; i < startPageNumber + pageLength; i++) {
            /* Get the byte position of the current page */
            bytePosition = (uint8_t)(i >> 5U);
            /* Get the bit position of the current page */
            bitPosition = i & 0x1FU;
            if (area == PFLASH_BLOCK0) {
                /* Set write protect mask */
                pflashWriteProtectMask[bytePosition] = pflashWriteProtectMask[bytePosition] | ((uint32_t)(1U << bitPosition));
                for (j = 0U; j < CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK / 32U; j++) {
                    FLASH_REG_EnablePflash0WriteProtection(regBase, (uint8_t)j, pflashWriteProtectMask[j]);
                }
            }
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
            else if (area == PFLASH_BLOCK1) {
                /* Set write protect mask */
                pflashWriteProtectMask[bytePosition] = pflashWriteProtectMask[bytePosition] | ((uint32_t)(1U << bitPosition));
                for (j = 0U; j < CONFIG_PFLASH_NUMBER_OF_PAGE / CONFIG_PFLASH_NUMBER_OF_BLOCK / 32U; j++) {
                    FLASH_REG_EnablePflash1WriteProtection(regBase, (uint8_t)j, pflashWriteProtectMask[j]);
                }
            }
#endif
            else {
                /* Set write protect mask */
                dflashWriteProtectMask[bytePosition] = dflashWriteProtectMask[bytePosition] | ((uint32_t)(1U << bitPosition));
                for (j = 0U; j < CONFIG_DFLASH_NUMBER_OF_PAGE / 32U; j++) {
                    FLASH_REG_EnableDflashWriteProtection(regBase, (uint8_t)j, dflashWriteProtectMask[j]);
                }
            }
        }
    } else {
        /* StartPageNumber + pageLength is out of range */
        errCode = ERR_FLASH_PARAMETER_ERROR;
    }

    return errCode;
}

#if (CONFIG_FLASH_READ_PROTECT_FEATURE == 1U)
/*******************************************************************************
 *** Function Name : FLASH_SetReadProtect
 *** Description   : Enable/Disable read protection
 ******************************************************************************/
errcode_t FLASH_SetReadProtect(bool enable)
{
    errcode_t errCode = ERR_SUCCESS;
    uint32_t optionByte[CONFIG_FLASH_RP_WP_OPB_SIZE / 4U] = {0x0U, 0x0U, 0x0U, 0x0U, 0x0U, 0x0U, 0x0U, 0x0U, 0x0U, 0x0U, 0x0U, 0x0U};

    FLASH_Unlock(FLASH_AREA_OPTION_BYTE);

    if (errCode == ERR_SUCCESS) {
        /* Record current option byte */
        FLASH_Read(FLASH_RP_WP_OPB_BASE_ADDRESS, (uint8_t*)optionByte, CONFIG_FLASH_RP_WP_OPB_SIZE);

        if (enable == true) {
            optionByte[0] = 0x0U;
        } else {
            optionByte[0] = FLASH_READ_PROTECTION_KEY;
        }

        /* Erase option byte before programing */
        errCode = FLASH_EraseOptionByte(FLASH_RP_WP_OPB_BASE_ADDRESS);

        if (errCode == ERR_SUCCESS) {
            /* Program option byte */
            errCode = FLASH_ProgramOptionByte(FLASH_RP_WP_OPB_BASE_ADDRESS,
                                              (uint8_t*)optionByte, CONFIG_FLASH_RP_WP_OPB_SIZE);
        }
    }

    FLASH_Lock(FLASH_AREA_OPTION_BYTE);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_GetReadProtectStatus
 *** Description   : Get current read protection status
 ******************************************************************************/
bool FLASH_GetReadProtectStatus(void)
{
    bool retValue;
    FLASH_Type* regBase = s_flashRegBase;

    retValue = FLASH_REG_GetReadStatus(regBase);

    return retValue;
}
#elif (CONFIG_FLASH_DEBUG_PROTECT_FEATURE == 1U)
/*******************************************************************************
 *** Function Name : FLASH_SetDebugProtect
 *** Description   : Enable/Disable debug protection
 ******************************************************************************/
errcode_t FLASH_SetDebugProtect(bool enable, flash_debug_auth_key_t key)
{
    errcode_t errCode = ERR_SUCCESS;
    uint32_t optionByte0[CONFIG_FLASH_RP_WP_OPB_SIZE / 4U];
    uint32_t optionByte1[CONFIG_FLASH_DGB_KEY_OPB_SIZE / 4U];

    FLASH_Unlock(FLASH_AREA_OPTION_BYTE);

    if (errCode == ERR_SUCCESS) {
        /* Record current option byte0 */
        FLASH_Read(FLASH_RP_WP_OPB_BASE_ADDRESS, (uint8_t*)optionByte0, CONFIG_FLASH_RP_WP_OPB_SIZE);

        if (enable == true) {
            optionByte0[0] = FLASH_READ_PROTECTION_KEY;
            optionByte1[0] = key.key0;
            optionByte1[1] = key.key1;
            optionByte1[2] = key.key2;
            optionByte1[3] = key.key3;
            optionByte1[4] = key.key4;
            optionByte1[5] = key.key5;
            optionByte1[6] = key.key6;
            optionByte1[7] = key.key7;

            /* Erase option byte before programing key */
            errCode = FLASH_EraseOptionByte(FLASH_DGB_KEY_OPB_BASE_ADDRESS);
            if (errCode == ERR_SUCCESS) {
                /* Program option byte: debug key */
                errCode = FLASH_ProgramOptionByte(FLASH_DGB_KEY_OPB_BASE_ADDRESS,
                                                  (uint8_t*)optionByte1, CONFIG_FLASH_DGB_KEY_OPB_SIZE);
            }
        } else {
            optionByte0[0] = 0xFFU;
        }

        if (errCode == ERR_SUCCESS) {
            /* Erase option byte before programing debug protect */
            errCode = FLASH_EraseOptionByte(FLASH_RP_WP_OPB_BASE_ADDRESS);

            if (errCode == ERR_SUCCESS) {
                /* Program option byte */
                errCode = FLASH_ProgramOptionByte(FLASH_RP_WP_OPB_BASE_ADDRESS,
                                                  (uint8_t*)optionByte0, CONFIG_FLASH_RP_WP_OPB_SIZE);
            }
        }
    }

    FLASH_Lock(FLASH_AREA_OPTION_BYTE);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_GetDebugStatus
 *** Description   : Get current read protection status
 ******************************************************************************/
bool FLASH_GetDebugStatus(void)
{
    bool retValue;
    FLASH_Type* regBase = s_flashRegBase;

    retValue = FLASH_REG_GetDebugStatus(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : FLASH_AuthenticateDebugKey
 *** Description   : Authentication debug key
 ******************************************************************************/
errcode_t FLASH_AuthenticateDebugKey(flash_debug_auth_key_t debugKey)
{
    errcode_t errCode;
    FLASH_Type* regBase = s_flashRegBase;

    FLASH_REG_AuthenticateDebugKey(regBase, debugKey);

    errCode = FLASH_REG_GetDebugStatus(regBase) ? ERR_SUCCESS : ERR_ERROR;

    return errCode;
}
#endif

#if (CONFIG_FLASH_AB_SWAP_FEATURE == 1U)
/*******************************************************************************
 *** Function Name : FLASH_SetAbSwap
 *** Description   : Enable/disable flash A/B swap function
 ******************************************************************************/
errcode_t FLASH_SetAbSwap(bool enable)
{
    errcode_t errCode;
    uint32_t optionByte[CONFIG_FLASH_AB_SWAP_OPB_SIZE / 4U];

    FLASH_Unlock(FLASH_AREA_OPTION_BYTE);

    /* Record current option byte */
    FLASH_Read(FLASH_AB_SWAP_OPB_BASE_ADDRESS, (uint8_t*)optionByte, CONFIG_FLASH_AB_SWAP_OPB_SIZE);

    if (enable == true) {
        optionByte[0] = FLASH_AB_SWAP_ENABLE_KEY;
    } else {
        optionByte[0] = 0xFF;
    }

    /* Erase option byte before programing swap enable key*/
    errCode = FLASH_EraseOptionByte(FLASH_AB_SWAP_OPB_BASE_ADDRESS);

    if (errCode == ERR_SUCCESS) {
        /* Program option byte */
        errCode = FLASH_ProgramOptionByte(FLASH_AB_SWAP_OPB_BASE_ADDRESS,
                                          (uint8_t*)optionByte, CONFIG_FLASH_AB_SWAP_OPB_SIZE);
    }

    FLASH_Lock(FLASH_AREA_OPTION_BYTE);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_GetAbSwapEnableStatus
 *** Description   : Get current A/B swap function status
 ******************************************************************************/
bool FLASH_GetAbSwapEnableStatus(void)
{
    bool retValue;
    FLASH_Type* regBase = s_flashRegBase;

    retValue = FLASH_REG_GetAbSwapEnableStatus(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : FLASH_SwapPflash
 *** Description   : Swap pflash block
 ******************************************************************************/
errcode_t FLASH_SwapPflash(void)
{
    errcode_t errCode = ERR_SUCCESS;
    uint32_t optionByte[CONFIG_FLASH_AB_SWAP_OPB_SIZE / 4U];
    uint8_t* tmpBuffer = (uint8_t*)optionByte;

    FLASH_Unlock(FLASH_AREA_OPTION_BYTE);

    /* Record current option byte */
    FLASH_Read(FLASH_AB_SWAP_OPB_BASE_ADDRESS, tmpBuffer, CONFIG_FLASH_AB_SWAP_OPB_SIZE);

    if (tmpBuffer[FLASH_OPTION_BYTE_SWAP_MAPPING_SEL] == FLASH_AB_SWAP_REMAP_B_KEY) {
        tmpBuffer[FLASH_OPTION_BYTE_SWAP_MAPPING_SEL] = (uint8_t)~FLASH_AB_SWAP_REMAP_B_KEY ;
    } else {
        tmpBuffer[FLASH_OPTION_BYTE_SWAP_MAPPING_SEL] = FLASH_AB_SWAP_REMAP_B_KEY ;
    }

    /* Erase option byte before programing */
    errCode = FLASH_EraseOptionByte(FLASH_AB_SWAP_OPB_BASE_ADDRESS);

    if (errCode == ERR_SUCCESS) {
        /* Program option byte */
        errCode = FLASH_ProgramOptionByte(FLASH_AB_SWAP_OPB_BASE_ADDRESS,
                                          tmpBuffer, CONFIG_FLASH_AB_SWAP_OPB_SIZE);
    }

    FLASH_Lock(FLASH_AREA_OPTION_BYTE);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_GetPflashSwapStatus
 *** Description   : Get pflash swapping mode(swapping or no swapping)
 ******************************************************************************/
flash_block_swap_t FLASH_GetPflashSwapStatus(void)
{
    flash_block_swap_t retValue;
    FLASH_Type* regBase = s_flashRegBase;

    retValue = FLASH_REG_IsPflahSwapping(regBase) ? PFLASH_BLOCK_SWAPPING : PFLASH_BLOCK_NO_SWAPPING;

    return retValue;
}
#endif

/*******************************************************************************
 *** Function Name : FLASH_GetChipUniqueIdentifier
 *** Description   : Get chip universally unique identifier
 ******************************************************************************/
void FLASH_GetChipUniqueIdentifier(flash_chip_uuid_type_t* uuid)
{
    FLASH_Type* regBase = s_flashRegBase;

    if (uuid != NULL) {
        uuid->lowUid = FLASH_REG_GetChipLowUniqueIdentifier(regBase);
        uuid->midLowUid = FLASH_REG_GetChipMidLowUniqueIdentifier(regBase);
        uuid->midHighUid = FLASH_REG_GetChipMidHighUniqueIdentifier(regBase);
        uuid->highUid = FLASH_REG_GetChipHighUniqueIdentifier(regBase);
    }
}

/*******************************************************************************
 *** Function Name : FLASH_GetChipMemorySizeInfo
 *** Description   : Get chip memory(pflash & sram) size information
 ******************************************************************************/
void FLASH_GetChipMemorySizeInfo(uint16_t* pflashSizeInfo, uint16_t* sramSizeInfo)
{
    FLASH_Type* regBase = s_flashRegBase;

    if (pflashSizeInfo != NULL) {
        *pflashSizeInfo = FLASH_REG_GetChipPflashSizeInfo(regBase);
    }

    if (sramSizeInfo != NULL) {
        *sramSizeInfo = FLASH_REG_GetChipSramSizeInfo(regBase);
    }
}

/*******************************************************************************
 *** Function Name : FLASH_GetChipPinInfo
 *** Description   : Get chip pins information
 ******************************************************************************/
void FLASH_GetChipPinInfo(uint16_t* pinInfo)
{
    FLASH_Type* regBase = s_flashRegBase;

    if (pinInfo != NULL) {
        *pinInfo = FLASH_REG_GetChipPinInfo(regBase);
    }
}

/*******************************************************************************
 *** Function Name : FLASH_GetBandgapOffset
 *** Description   : Get chip Bandgap offset
 ******************************************************************************/
int16_t FLASH_GetBandgapOffset(void)
{
    FLASH_Type* regBase = s_flashRegBase;
    int16_t bandgapOffset;

    bandgapOffset = FLASH_REG_GetBandgapOffset(regBase);

    return bandgapOffset;
}

/*******************************************************************************
 *** Function Name : FLASH_Erase
 *** Description   : Erase the speified area according to the command
 ******************************************************************************/
static errcode_t FLASH_Erase(FLASH_Type* regBase, uint32_t address, uint8_t eraseCmd)
{
    ASSERT_PARAM(eraseCmd > 0U);
    ASSERT_PARAM(eraseCmd < 4U);

    errcode_t errCode = ERR_SUCCESS;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif

    /* Wait for last operation to complete */
    (void)FLASH_WaitOperateComplete(regBase);

    /* Clear error flags before erasing */
    FLASH_REG_ClearAllErrFlag(regBase);

#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    FLASH_REG_SafetyUnlock(regBase);

    /* Get control of flash controller */
    do {
        FLASH_REG_GetControlOfFlashController(regBase);
        timeout--;
    } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

    /* Check whether control of the Flash controller has been got */
    if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
        /* Set erase address and cmd */
        FLASH_REG_SetCmdAddress(regBase, address);
        FLASH_REG_SetFlashCmd(regBase, eraseCmd);

        /* Start command, erasing begin */
        errCode = FLASH_StartCmd(regBase);
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    } else {
        errCode = ERR_ERROR;
    }
#endif

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_Verify
 *** Description   : Verify the speified area according to the command
 ******************************************************************************/
static errcode_t FLASH_Verify(FLASH_Type* regBase, uint32_t address, uint8_t eraseCmd)
{
    errcode_t errCode;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif

    /* Wait for last operation to complete */
    (void)FLASH_WaitOperateComplete(regBase);

    /* Clear error flags before verifying */
    FLASH_REG_ClearAllErrFlag(regBase);

#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    FLASH_REG_SafetyUnlock(regBase);

    /* Get control of flash controller */
    do {
        FLASH_REG_GetControlOfFlashController(regBase);
        timeout--;
    } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

    /* Check whether control of the Flash controller has been got */
    if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
        /* Set verify address and cmd */
        FLASH_REG_SetCmdAddress(regBase, address);
        FLASH_REG_SetFlashCmd(regBase, eraseCmd);

        /* Start command, verifying begin */
        errCode = FLASH_StartCmd(regBase);
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    } else {
        errCode = ERR_ERROR;
    }
#endif

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_StartCmd
 *** Description   : Start commad and wait for completion
 ******************************************************************************/
static errcode_t FLASH_StartCmd(FLASH_Type* regBase)
{
    errcode_t errCode = ERR_SUCCESS;

    /* Start command */
    FLASH_REG_StartFlashCmd(regBase);

    errCode = FLASH_WaitOperateComplete(regBase);

    FLASH_REG_SafetyLock(regBase);

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_StartCmd
 *** Description   : Start commad and wait for completion
 ******************************************************************************/
static errcode_t FLASH_WaitOperateComplete(FLASH_Type* regBase)
{
    errcode_t errCode = ERR_SUCCESS;
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;


    while ((FLASH_REG_GetCmdCompleteFlag(regBase) == false) && (timeout > 0U)) {
        timeout--;
    }

    if (timeout == 0U) {
        errCode = ERR_TIMEOUT;
    } else {
        /* Flash command finished , check whether any errors have occurred*/
        if (FLASH_REG_GetCmdAccessErrFlag(regBase) == true || FLASH_REG_GetCmdCompletionErrFlag(regBase) == true) {
            /* Check flash cmd error flag */
            errCode = ERR_ERROR;
#if (CONFIG_FLASH_READ_PROTECT_FEATURE == 1U)
        } else if (FLASH_REG_GetReadProtectionErrFlag(regBase) == true) {
            /* Check read protection error flag */
            errCode = ERR_FLASH_READ_PROTECTION_ERROR;
#endif
        } else if (FLASH_REG_GetWriteProtectionErrFlag(regBase) == true) {
            /* Check write protection error flag */
            errCode = ERR_FLASH_WRITE_PROTECTION_ERROR;
        } else if (FLASH_REG_GetOptonByteErrFlag(regBase) == true) {
            /* Check option byte error flag */

        } else {
            /* No errors occur */
            errCode = ERR_SUCCESS;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_AsyncProgramSection
 *** Description   : Program setction asynchronously
 ******************************************************************************/
errcode_t FLASH_AsyncProgramSection(uint32_t startAddress, uint8_t* buffer, uint16_t length)
{
    FLASH_Type* regBase = s_flashRegBase;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t lowWord;
    uint32_t highWord;
    uint16_t i;
    uint8_t lengthOfPhrase;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif

    if (FLASH_REG_GetCmdCompleteFlag(regBase)) {
        /* Clear error flags before programing */
        FLASH_REG_ClearAllErrFlag(regBase);

#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        FLASH_REG_SafetyUnlock(regBase);

        /* Get control of flash controller */
        do {
            FLASH_REG_GetControlOfFlashController(regBase);
            timeout--;
        } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

        /* Check whether control of the Flash controller has been got */
        if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
            FLASH_REG_ClearPointOfInternelBuffer(regBase);

            /* Set cmd programing address and length of phrase*/
            FLASH_REG_SetFlashCmd(regBase, FLASH_PROGRAM_SECTION_CMD);
            FLASH_REG_SetCmdAddress(regBase, startAddress);
            lengthOfPhrase = (uint8_t)(length / FLASH_PHRASE_SIZE);
            FLASH_REG_SetProgramLengthOfPhrase(regBase, (lengthOfPhrase - 1U));

            for (i = 0; i < length; i = i + 8) {
                /* Write data to flash internal buffer */
                lowWord = (uint32_t)(buffer[i + 3] << 24U) | (uint32_t)(buffer[i + 2] << 16U)
                          | (uint32_t)(buffer[i + 1] << 8U) | (uint32_t)buffer[i];
                highWord = (uint32_t)(buffer[i + 7] << 24U) | (uint32_t)(buffer[i + 6] << 16U)
                           | (uint32_t)(buffer[i + 5] << 8U) | (uint32_t)buffer[i + 4];
                FLASH_REG_WriteLowWord(regBase, lowWord);
                FLASH_REG_WriteHighWord(regBase, highWord);
            }

            /* Start command, programing begin */
            FLASH_REG_StartFlashCmd(regBase);
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        } else {
            errCode = ERR_ERROR;
        }
#endif
    } else {
        errCode = ERR_BUSY;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_AsyncErasePage
 *** Description   : Erase the page asynchronously
 ******************************************************************************/
errcode_t FLASH_AsyncErasePage(uint32_t pageAddress)
{
    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif

    if (FLASH_REG_GetCmdCompleteFlag(regBase)) {
        /* Clear error flags before erasing */
        FLASH_REG_ClearAllErrFlag(regBase);

#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        FLASH_REG_SafetyUnlock(regBase);

        /* Get control of flash controller */
        do {
            FLASH_REG_GetControlOfFlashController(regBase);
            timeout--;
        } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

        /* Check whether control of the Flash controller has been got */
        if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
            /* Set erase address and cmd */
            FLASH_REG_SetCmdAddress(regBase, pageAddress);
            FLASH_REG_SetFlashCmd(regBase, FLASH_ERASE_PAGE_CMD);

            /* Start command, erasing begin */
            FLASH_REG_StartFlashCmd(regBase);
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        } else {
            errCode = ERR_ERROR;
        }
#endif
    } else {
        errCode = ERR_BUSY;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_AsyncEraseBlock
 *** Description   : Erase the block asynchronously
 ******************************************************************************/
errcode_t FLASH_AsyncEraseBlock(flash_block_type_t block)
{
    errcode_t errCode = ERR_SUCCESS;
    uint32_t address = 0U;
    FLASH_Type* regBase = s_flashRegBase;
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
    volatile uint32_t timeout = FLASH_HW_DEADLINE_TIMEOUT;
#endif
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    uint16_t pflashSize;
#endif

    if (block == PFLASH_BLOCK0) {
        address = FLASH_PFLASH_BASE_ADDRESS;
#if (CONFIG_PFLASH_NUMBER_OF_BLOCK == 2U)
    } else if (block == PFLASH_BLOCK1) {
        pflashSize = FLASH_REG_GetChipPflashSizeInfo(regBase);

        if (pflashSize == 0x400) {
            /* For 1MB Flash */
            address = FLASH_PFLASH_BASE_ADDRESS + FLASH_PFLASH_BLOCK_SIZE_FOR_1M;
        } else if (pflashSize == 0x200) {
            /* For 512KB Flash */
            address = FLASH_PFLASH_BASE_ADDRESS + FLASH_PFLASH_BLOCK_SIZE_FOR_512K;
        } else {
            /* Nothing to do */
        }
#endif
    } else {
        address = FLASH_DFLASH_BASE_ADDRESS;
    }

    if (FLASH_REG_GetCmdCompleteFlag(regBase)) {
        /* Clear error flags before erasing */
        FLASH_REG_ClearAllErrFlag(regBase);

#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        FLASH_REG_SafetyUnlock(regBase);

        /* Get control of flash controller */
        do {
            FLASH_REG_GetControlOfFlashController(regBase);
            timeout--;
        } while (!FLASH_REG_IsHostCpuOperation(regBase) && (timeout > 0U));

        /* Check whether control of the Flash controller has been got */
        if (FLASH_REG_IsHostCpuOperation(regBase) == true) {
#endif
            /* Set erase address and cmd */
            FLASH_REG_SetCmdAddress(regBase, address);
            FLASH_REG_SetFlashCmd(regBase, FLASH_ERASE_BLOCK_CMD);

            /* Start command, erasing begin */
            FLASH_REG_StartFlashCmd(regBase);
#if (CONFIG_FLASH_CHIP_MULTI_MASTER == 1U)
        } else {
            errCode = ERR_ERROR;
        }
#endif
    } else {
        errCode = ERR_BUSY;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : FLASH_GetAsyncOperateStatus
 *** Description   : Gets the status of the asynchronous operation
 ******************************************************************************/
errcode_t FLASH_GetAsyncOperateStatus(void)
{
    errcode_t errCode = ERR_SUCCESS;
    FLASH_Type* regBase = s_flashRegBase;


    if (FLASH_REG_GetCmdCompleteFlag(regBase)) {
        FLASH_REG_SafetyLock(regBase);
        /* Flash command finished , check whether any errors have occurred*/
        if (FLASH_REG_GetCmdAccessErrFlag(regBase) == true || FLASH_REG_GetCmdCompletionErrFlag(regBase) == true) {
            /* Check flash cmd error flag */
            errCode = ERR_ERROR;
        } else {
            /* No errors occur */
            errCode = ERR_SUCCESS;
        }
    } else {
        errCode = ERR_BUSY;
    }

    return errCode;
}

/*******EOF********************************************************************/
